#include <cstdio>
#include <iostream>
#include <fstream>
#include <string>
#include <QStringList>

#include "networktests.h"

NetworkTests::NetworkTests(QObject *parent) :
    QObject(parent)
{
}

/* runPingTest
 *
 * Desc - Starts the ping test
 */
void NetworkTests::runPingTest(int *ipAddress)
{
    // Ref (not using) : http://stackoverflow.com/questions/309491/how-do-i-read-the-results-of-a-system-call-in-c
    // Ref (using) : http://www.linuxquestions.org/questions/programming-9/c-c-popen-launch-process-in-specific-directory-620305/

    QString pingCommand, data, copyOfData, secondCopyOfData, pingResultString;
    FILE *stream;
    int MAX_BUFFER = 256, lastLineLHS, packetLossLHS, packetLossRHS, packetLossPercentage;
    float pingResult;
    char buffer[MAX_BUFFER];

    // DEBUG
    //emit DEBUG_NetworkTestThreadMessage("NetworkTestThread - Start");

    this->ipAddress[0] = *(ipAddress);
    this->ipAddress[1] = *(ipAddress + 1);
    this->ipAddress[2] = *(ipAddress + 2);
    this->ipAddress[3] = *(ipAddress + 3);

    delete ipAddress;

    pingCommand = "ping -w 1 ";

    // Add the IP address to the command call
    for(int i = 0; i < 4; i++){
        pingCommand += QString::number(this->ipAddress[i]);
        pingCommand += ".";
    }

    // Strip the last "." off the end of the command string
    pingCommand = pingCommand.left(pingCommand.length() - 1);

    /* Format the command string, and run the process, putting
     * the results into a string as they come in through a stream */
    pingCommand.append(" 2>&1");
    stream = popen(pingCommand.toStdString().c_str(), "r");
    if (stream){
        while (!feof(stream)){
                if (fgets(buffer, MAX_BUFFER, stream) != NULL){
                        data.append(buffer);
                }
        }
        pclose(stream);
    } else {
        // Error getting text stream from process call
    }

    // Get the results we want from the end of the result of the process call
    //
    // Need to duplicate the data string, as the methods used to extract the
    // data are far simpler for finding both packet loss, and ping average,
    // when the data string is allowed to be corrupted at the end of finding
    // either data point
    copyOfData = data;
    secondCopyOfData = data;

    // Check to see how much packet loss there was, in case no packets
    // got through
    packetLossRHS = copyOfData.indexOf("% packet loss, ");
    packetLossLHS = copyOfData.indexOf("received, ");

    copyOfData.chop(copyOfData.length() - packetLossRHS);

    // NOTE - The constant 10 here is the length of the string "received, "
    packetLossPercentage = copyOfData.right(copyOfData.length() - (packetLossLHS + 10)).toInt();

    if(packetLossPercentage==100 || (secondCopyOfData.contains("unreachable")) || (secondCopyOfData.contains("Unreachable"))){
        emit pingTestCompleted(TEST_FAILURE);
    } else {
        // Trim the string down to the section which contains the statistics
        lastLineLHS = data.lastIndexOf("=");
        data = data.right(data.length() - lastLineLHS);

        pingResultString = data.split("/")[1];
        pingResult = pingResultString.toFloat();

        emit pingTestCompleted(pingResult);
   }

   // DEBUG
   //emit DEBUG_NetworkTestThreadMessage("NetworkTestThread - End");
}

/*
  runNetworkInterfaceTest

  Desc - Returns the ip address of the network interface, or -1 in every
         element if the network interface has no address
  */
void NetworkTests::runNetworkInterfaceTest(QString interfaceName)
{
    // Ref (not using) : http://stackoverflow.com/questions/309491/how-do-i-read-the-results-of-a-system-call-in-c
    // Ref (using) : http://www.linuxquestions.org/questions/programming-9/c-c-popen-launch-process-in-specific-directory-620305/

    QString pingCommand, data;
    QStringList ipValues;
    FILE *stream;
    int MAX_BUFFER = 256, ipAddressStart, ipAddressEnd, *ipFromData, *failIP;
    char buffer[MAX_BUFFER];

    // DEBUG
    //emit DEBUG_NetworkTestThreadMessage("NetworkTestThread - Start");

    pingCommand = "ifconfig " + interfaceName;

    /* Format the command string, and run the process, putting
     * the results into a string as they come in through a stream */
    pingCommand.append(" 2>&1");
    stream = popen(pingCommand.toStdString().c_str(), "r");
    if (stream){
        while (!feof(stream)){
                if (fgets(buffer, MAX_BUFFER, stream) != NULL){
                        data.append(buffer);
                }
        }
        pclose(stream);
    } else {
        // Error getting text stream from process call
    }

    if(data.contains("Device not found")){
        failIP = new int[4];
        *(failIP) = GCSCommon::DoesNotExist;
        *(failIP + 1) = GCSCommon::DoesNotExist;
        *(failIP + 2) = GCSCommon::DoesNotExist;
        *(failIP + 3) = GCSCommon::DoesNotExist;

        emit networkInterfaceTestCompleted(failIP);
    } else if(data.contains("inet addr:")){
        // Get the IP address from the data string

        ipAddressStart = data.indexOf("inet addr:");
        // NOTE - X is the length of the string "inet addr:"
        ipAddressStart += 10;
        data = data.right(data.length() - ipAddressStart);
        ipAddressEnd = data.indexOf(" Bcast");
        data.chop(data.length() - ipAddressEnd);

        // Get the value for each number in the IP address
        ipValues = data.split(".");
        ipFromData = new int[4];
        *(ipFromData) = ipValues.at(0).toInt();
        *(ipFromData + 1) = ipValues.at(1).toInt();
        *(ipFromData + 2) = ipValues.at(2).toInt();
        *(ipFromData + 3) = ipValues.at(3).toInt();

        emit networkInterfaceTestCompleted(ipFromData);

    } else {
        failIP = new int[4];
        *(failIP) = GCSCommon::NoIP;
        *(failIP + 1) = GCSCommon::NoIP;
        *(failIP + 2) = GCSCommon::NoIP;
        *(failIP + 3) = GCSCommon::NoIP;

        emit networkInterfaceTestCompleted(failIP);
    }

   // DEBUG
   //emit DEBUG_NetworkTestThreadMessage("NetworkTestThread - End");
}

/* runDNSTest
 *
 * Desc - Starts the DNS ip test
 */
void NetworkTests::runDNSTest()
{
    // Ref (not using) : http://stackoverflow.com/questions/309491/how-do-i-read-the-results-of-a-system-call-in-c
    // Ref (using) : http://www.linuxquestions.org/questions/programming-9/c-c-popen-launch-process-in-specific-directory-620305/

    QString pingCommand, data;
    FILE *stream;
    int MAX_BUFFER = 256;
    char buffer[MAX_BUFFER];

    // DEBUG
    //emit DEBUG_NetworkTestThreadMessage("NetworkTestThread - Start");

    // Checks to see if the computer can get the IP address for google.com.au
    pingCommand = "cd /tmp/ && host -W 1 google.com.au";

    /* Format the command string, and run the process, putting
     * the results into a string as they come in through a stream */
    pingCommand.append(" 2>&1");
    stream = popen(pingCommand.toStdString().c_str(), "r");
    if (stream){
        while (!feof(stream)){
                if (fgets(buffer, MAX_BUFFER, stream) != NULL){
                        data.append(buffer);
                }
        }
        pclose(stream);
    } else {
        // Error getting text stream from process call
    }

    if(data.contains("no servers could be reached")){
        emit DNSTestCompleted(false);
    } else {
        emit DNSTestCompleted(true);
    }

   // DEBUG
   //emit DEBUG_NetworkTestThreadMessage("NetworkTestThread - End");
}
